package com.huanletao.proxy.cglib;

import com.huanletao.proxy.AlterStaticMethodInterceptor;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * <p>
 * </p>
 * package: com.huanletao.proxy.cglib
 * description:
 * className: ProxyUtils
 * author:  踏雪无痕
 * copyright: Copyright (c) 2022/8/26 20:16
 * since: 1.0.0
 */
public abstract class ProxyCreator {

    private static final Enhancer enhancer = new Enhancer();

    /**
     * 获取代理对象；
     *
     * @param advisor 拦截建议其
     * @param clazz   代理class
     * @param <T>
     * @return
     */
    @SuppressWarnings("all")
    public static <T> T createCglibProxy(Advisor advisor, Class<T> clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback((MethodInterceptor) (obj, method, objects, methodProxy) -> {
            boolean matches = advisor.matches(method, clazz);
            if (matches) {
                MethodInvocation invocation = new CGlibMethodInvocation(objects, methodProxy, obj);
                return runProxy(advisor, invocation);
            }
            return methodProxy.invokeSuper(obj, objects);
        });
        T proxyObj = (T) enhancer.create();
        return proxyObj;
    }

    /**
     * 创建 jdk 代理。
     *
     * @param advisor
     * @param targetClass 代理类class
     * @param <T>
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    @SuppressWarnings("all")
    public static <T> T createJdkProxy(Advisor advisor, Class<T> targetClass) throws Throwable  {
        T target = targetClass.newInstance();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        T proxyInstance = (T) Proxy.newProxyInstance(classLoader,
                targetClass.getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        boolean matches = advisor.matches(method, targetClass);
                        if (matches) {
                            MethodInvocation invocation = new JdkMethodInvocation(args, target, method);
                            return runProxy(advisor, invocation);
                        }
                        return method.invoke(target, args);
                    }
                });

        return proxyInstance;
    }

    /**
     * run proxy
     *
     * @param advisor
     * @param invocation
     * @return
     */
    private static Object runProxy(Advisor advisor, MethodInvocation invocation) throws Throwable {
        Object result = null;
        Interceptor interceptor = advisor.getInterceptor();
        if (interceptor instanceof BeforeStaticMethodInterceptor) {
            ((BeforeStaticMethodInterceptor) interceptor).before();
        }
        if (interceptor instanceof StaticMethodInterceptor) {
            result = ((StaticMethodInterceptor) interceptor).invoke(invocation);
        }
        if (interceptor instanceof AlterStaticMethodInterceptor) {
            ((AlterStaticMethodInterceptor) interceptor).alter();
        }
        return result;
    }

    public static ProxyCreator createCglibProxy(MethodNameAdvisor advisor, String clazz) {
        System.out.println("advisor = " + advisor);
        return null;
    }
}
